import("System")
import("System.IO.Ports")
import("Jodell.Tool", "Jodell.Tool")

function intToBinStr(x)
    local t = {}
    while x > 0 do
        local rest = x % 2
        table.insert(t, 1, rest) -- 在表的开头插入余数
        x = math.floor(x / 2)
    end
    if #t == 0 then t[1] = 0 end -- 如果数字是0，则直接返回0
    return table.concat(t)
end

local Lua_ok = 0
local Lua_Error_Notconnect = -1
local Lua_Error_TimeOut = -2
local Lua_Error_DeviceTypeNotEqual = 3
local Lua_Error_change500 = 4
local Lua_Error_Change501 = 5

local versionRegAddr = 558
local saveRegAddr = 1001
local restoreRegAddr = 1002
local caliEncRegAddr = 1004
local searchZeroRegAddr = 1005

local firmwarePath = "./EPG22-300_APP_AES_0_9_23.bin"
local slaveID = 1
local targetVersion = "0.9.23"

local versionCheckBefore = true -- check version before upgrade
local updateArgs = true
local restoreArgs = true
local setArgs = false
local calibrateEncoder = true
local searchZero = true

function updateArgsFun(client, slaveID)
    if (restoreArgs) then
        local ret = client:WriteUInt16(slaveID, restoreRegAddr, 1)
        if (ret == 0) then
            Info("恢复默认成功！")
        else
            Info(string.format("恢复默认失败：%d", ret))
        end
    end

    local ret = client:WriteUInt16(slaveID, saveRegAddr, 1)
    if (ret == 0) then
        Info("保存成功！")
    else
        Info(string.format("保存失败：%d", ret))
    end
end

function calibrateEncoderFun(client, slaveID)
    local ret = client:WriteUInt16(slaveID, caliEncRegAddr, 1)
    if (ret == 0) then
        Info("编码器校准中，请等待大约20s...")
        Delay(20000)
        if (readVersion(client, slaveID) ~= nil) then
            Info("编码器校准成功！")
        else
            Info("无法读取版本，编码器校准失败!")
        end
    else
        Info(string.format("编码器校准指令下发失败：%d", ret))
    end
end

function searchZeroFun(client, slaveID)
    local ret = client:WriteUInt16(slaveID, searchZeroRegAddr, 1)
    if (ret == 0) then
        Info("对齐找零中，请等待大约5s...")
        Delay(5000)
        if (readVersion(client, slaveID) ~= nil) then
            Info("对齐找零成功！")
        else
            Info("无法读取版本，对齐找零失败!")
        end
    else
        Info(string.format("对齐找零指令下发失败：%d", ret))
    end
end

function num2version(major, minor, revision)
    return string.format("%d.%d.%d", major, minor, revision)
end

function readVersion(client, slaveID)
    local ret, data = client:ReadHoldingRegisters(slaveID, versionRegAddr, 3)
    if (ret == 0) then
        local version = num2version((data[0] << 8) + data[1], (data[2] << 8) + data[3], (data[4] << 8) + data[5])
        Info(string.format("设备版本号：%s", version))
        return version
    else
        Info(string.format("无法读取版本号：%d", ret))
        return nil
    end
end

client = Lua_ModbusRtuClient()
client.PortName = "COM4"	
client:Connect()

local deviceVersion = readVersion(client, slaveID)
local newVersion = deviceVersion

if (deviceVersion ~= nil) then
    if ((not versionCheckBefore) or (versionCheckBefore and deviceVersion ~= targetVersion)) then
        Info("固件写入中，请耐心等待，大概10s...")
        local res = client:WriteFile(slaveID, 0, firmwarePath)
        if (res == Lua_ok) then
            Info("固件写入成功！")
        else
            Info(string.format("固件写入失败：%d", res))
        end

        Info("等待重启...")
        Delay(2000)
        newVersion = readVersion(client, slaveID)
    end

    if (newVersion ~= nil) then
        if (newVersion == targetVersion) then
            Info("升级成功------------------！")

            if (searchZero and (not calibrateEncoder)) then
                searchZeroFun(client, slaveID)
            end

            if (calibrateEncoder) then
                calibrateEncoderFun(client, slaveID)
            end

            if (updateArgs) then
                updateArgsFun(client, slaveID)
            end
        else
            Info("升级失败xxxxxxxxxxxxxxxxxx！")
        end
        --打开测试模式
        client:WriteUInt16(1, 1003,  1)
        client:WriteUInt16(1, 1024,  1)
        for i = 0, 9 do
            if (IsCancel) then
                break
            end
		    client:WriteUInt16(1, 1018 + i % 2,  1)	
    		if (i % 2 == 0) then
        		Info(string.format("\t打开%d" , i // 2 + 1))
    		else
        		Info(string.format("\t闭合%d" , i // 2 + 1))
   		    end
		    Delay(400)
	    end  
        -- 数字输出跑马灯
        Info("--数字输出跑马灯---")
        for i = 0, 8 do
            if (IsCancel) then
                break
            end
            local value = 1 << (i % 3)
            local ret = client:WriteUInt16(1, 1007, value)
            Info(string.format("\t输出信号%d, %s", ret, intToBinStr(value)))
            Delay(500)	
        end   
        Info("--数字输入<-->输出映射---")
        local input_value = 0xFFFF
        for i = 0, 10000 do
            if (IsCancel) then
                break
            end
            local ret, value = client:ReadUInt16(1, 2001)
            if (input_value ~= value) then
                input_value = value
                Info(string.format("\t输入信号%d, %s" , ret, intToBinStr(input_value)))
                Delay(50)
                client:WriteUInt16(1, 1007,  input_value)
            end
            Delay(50)	
        end
    end
end
--关闭测试模式
client:WriteUInt16(1, 1003,  0)

client:Disconnect()
